package com.ichir.projects.uiflow;

import java.util.Map;

import com.ichir.projects.uiflow.definition.IFlowDefinition;
import com.ichir.projects.uiflow.definition.IFlowPart;
import com.ichir.projects.uiflow.definition.ITransition;
import com.ichir.projects.uiflow.events.IUiPartListener;
import com.ichir.projects.uiflow.internal.impl.DefaultFlowPartImpl;
import com.ichir.projects.uiflow.internal.impl.DefaultUiViewImpl;
import com.ichir.projects.uiflow.internal.resolvers.TransitionResolver;

/**
 * 
 * UI Framework bootstrap: main starting flow point.
 * 
 * @author mahieddine
 * @since 1.0.0
 */
public class UiFlowBootstrap {

	/**
	 * bootstrap instance.
	 */
	private static UiFlowBootstrap instance;
	private DefaultUiViewImpl defaultUiViewImpl;

	/**
	 * Base constructor: singleton.
	 */
	private UiFlowBootstrap() {
	}

	/**
	 * @return Singleton instance
	 */
	public static UiFlowBootstrap getInstance() {
		if (instance == null) {
			instance = new UiFlowBootstrap();
		}
		return instance;
	}

	/**
	 * Start bootstrap.
	 * @param flow flow definition
	 * @param attributes starting flow attributes, will be available in the {@link ApplicationContext}
	 * @throws Exception 
	 */
	public void start (IFlowDefinition flow, Map<String, Object> attributes) throws Exception {

		/**
		 * Application context.
		 */
		ApplicationContext context;
		if (attributes == null) {
			context = new ApplicationContext();
		} else {
			context = new ApplicationContext(attributes);
		}

		// get properties defined in flow
		context.putAll(flow.getProperties());
		
		// initialize flow transitions
		FlowHandler handler = new FlowHandler();
		
		// initialize pre-starting point
		IFlowPart initialFlowPart = getInitialFlowPart(flow, handler);
		TransitionResolver transitionResolver = new TransitionResolver(initialFlowPart);
		transitionResolver.setFlowDefinition(flow);

		defaultUiViewImpl.addHandledListener(handler);
		// initialize flow handler
		handler.setTransitionResolver(transitionResolver);
		handler.setContext(context);

		// start flow
		defaultUiViewImpl.refreshView(context, null);
	}

	/**
	 * @param flowDefinition
	 * @return initial / fake flow part to automatically manage transition to starting flow part
	 */
	private IFlowPart getInitialFlowPart(final IFlowDefinition flowDefinition, IUiPartListener listener) {
		// flow starting point: fake action to go to starting view
		defaultUiViewImpl = new DefaultUiViewImpl("next");
		//final DefaultUiActionImpl defaultActionImpl = new DefaultUiActionImpl("next");
		// wrapper for a fake flow part wrapping starting view as a targeted flow part
		DefaultFlowPartImpl preStartupFlowPart = new DefaultFlowPartImpl(defaultUiViewImpl, new ITransition() {
			@Override
			public IFlowPart getTo() {
				return flowDefinition.getStart();
			}
			
			@Override
			public String getId() {
				return defaultUiViewImpl.getEventId();
			}
			
			@Override
			public boolean disposeView() {
				return false;
			}
		});
		defaultUiViewImpl.addHandledListener(listener);
		
		return preStartupFlowPart;
	}
	
}
